﻿using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Threading.Tasks;
using DoubanFM.Common;
using DoubanFM.ViewModel;
using Microsoft.PlayerFramework;
using Windows.Media;
using Windows.UI.Core;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Media;
using Windows.UI.Xaml.Media.Animation;

namespace DoubanFM.View
{
    /// <summary>
    /// A basic page that provides characteristics common to most applications.
    /// </summary>
    public sealed partial class ChannelPage
    {
        // Singleton datasource instance
        private readonly DataSource _dataSource = DataSource.Instance;

        /// <summary>
        /// Constructor
        /// </summary>
        public ChannelPage()
        {
            InitializeComponent();
            // LongkerDandy: seems xaml doesn't handle escape unicode string well, so I put it here
            musicAlbumPrefix.Text = "\u4E13\u8F91" + ":";
            musicYearPrefix.Text = "\u53D1\u884C\u65F6\u95F4" + ":";
            // Music Title animation
            musicTitle.SizeChanged += MusicTitleSizeChanged;
            // Player event
            player.MediaStarted += PlayerMediaStarted;
            player.MediaEnded += PlayerMediaEnded;
            player.MediaFailed += PlayerMediaFailed;
            player.CurrentStateChanged += PlayerCurrentStateChanged;
            // MediaControl event, required for background playing
            MediaControl.PlayPressed += MediaControlPlayPressed;
            MediaControl.PausePressed += MediaControlPausePressed;
            MediaControl.PlayPauseTogglePressed += MediaControlPlayPauseTogglePressed;
            MediaControl.StopPressed += MediaControlStopPressed;
            MediaControl.NextTrackPressed += MediaControlNextTrackPressed;
            MediaControl.PreviousTrackPressed += MediaControlPreviousTrackPressed;
            // Music list event
            _dataSource.MusicList.CollectionChanged += MusicListCollectionChanged;
        }

        #region Event Handler

        /// <summary>
        /// Populates the page with content passed during navigation.  Any saved state is also
        /// provided when recreating a page from a prior session.
        /// </summary>
        /// <param name="navigationParameter">The parameter value passed to
        /// <see cref="Frame.Navigate(Type, Object)"/> when this page was initially requested.
        /// </param>
        /// <param name="pageState">A dictionary of state preserved by this page during an earlier
        /// session.  This will be null the first time a page is visited.</param>
        protected override void LoadState(Object navigationParameter, Dictionary<String, Object> pageState)
        {
            // Allow saved page state to override the initial item to display
            if (pageState != null && pageState.ContainsKey("CurrentChannel"))
            {
                navigationParameter = pageState["CurrentChannel"];
            }

            // Channel ID from parameter
            var channel = _dataSource.GetChannel((String)navigationParameter);
            SetCurrentChannel(channel);
            var channelId = channel != null ? channel.UniqueId : "1";  // Default to channel 1, but this shouldn't happen

            // Get new music list
            _dataSource.NewMusicList(channelId);
        }

        /// <summary>
        /// Preserves state associated with this page in case the application is suspended or the
        /// page is discarded from the navigation cache.  Values must conform to the serialization
        /// requirements of <see cref="SuspensionManager.SessionState"/>.
        /// </summary>
        /// <param name="pageState">An empty dictionary to be populated with serializable state.</param>
        protected override void SaveState(Dictionary<String, Object> pageState)
        {
            pageState["CurrentChannel"] = DefaultViewModel["CurrentChannel"];
        }

        /// <summary>
        /// Player MediaStarted state handler
        /// </summary>
        /// <param name="sender">Event sender</param>
        /// <param name="e">Event arguments</param>
        private void PlayerMediaStarted(object sender, RoutedEventArgs e)
        {
            Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
            {
                var music = GetCurrentMusic();
                if (music != null)
                {
                    MediaControl.ArtistName = music.Artist;
                    MediaControl.TrackName = music.Title;
                    // TODO: It seems not work
                    MediaControl.AlbumArt = new Uri("ms-appx:///" + music.SmallImagePath);
                }
            });
        }

        /// <summary>
        /// Player MediaFailed state handler
        /// Play next music
        /// </summary>
        /// <param name="sender">Event sender</param>
        /// <param name="e">Event arguments</param>
        private void PlayerMediaFailed(object sender, ExceptionRoutedEventArgs e)
        {
            // TODO: log error

            // Remove last music from list
            if (_dataSource.MusicList.Count > 0)
            {
                _dataSource.MusicList.RemoveAt(0);
            }

            // Play next
            PlayNext();
        }

        /// <summary>
        /// Player MediaEnded state handler
        /// Add to history & play next music
        /// </summary>
        /// <param name="sender">Event sender</param>
        /// <param name="e">Event arguments</param>
        private void PlayerMediaEnded(object sender, MediaPlayerActionEventArgs e)
        {
            // Remove last music from list and add to history
            if (_dataSource.MusicList.Count > 0)
            {
                _dataSource.AddToMusicHistory(_dataSource.MusicList[0]);
                _dataSource.MusicList.RemoveAt(0);
            }

            // Play next
            PlayNext();
        }

        /// <summary>
        /// Player current state changed event handler
        /// </summary>
        /// <param name="sender">Event sender</param>
        /// <param name="e">Event arguments</param>
        private void PlayerCurrentStateChanged(object sender, RoutedEventArgs e)
        {
            // Change PlayPauseButton style
            switch (player.CurrentState)
            {
                case MediaElementState.Playing:
                    PlayPauseButton.Style = (Style)Application.Current.Resources["PauseButtonStyle"];
                    break;
                case MediaElementState.Closed:
                case MediaElementState.Paused:
                    PlayPauseButton.Style = (Style)Application.Current.Resources["PlayButtonStyle"];
                    break;
            }
        }

        /// <summary>
        /// PlayPauseButton click handler
        /// Play or Pause music
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void PlayPauseButtonClick(object sender, RoutedEventArgs e)
        {
            switch (player.CurrentState)
            {
                case MediaElementState.Playing:
                    player.Pause();
                    break;
                case MediaElementState.Stopped:
                case MediaElementState.Paused:
                    player.Play();
                    break;
            }
        }

        /// <summary>
        /// SkipAheadButton click handler
        /// Play or Pause music
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void SkipAheadButtonClick(object sender, RoutedEventArgs e)
        {
            // Remove last music from list
            if (_dataSource.MusicList.Count > 0)
            {
                _dataSource.MusicList.RemoveAt(0);
            }

            // Play next
            PlayNext();
        }

        private void MediaControlPausePressed(object sender, object e)
        {
            Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () => player.Pause());
        }

        private void MediaControlPlayPressed(object sender, object e)
        {
            Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () => player.Play());
        }

        private void MediaControlPreviousTrackPressed(object sender, object e)
        {
            // LongkerDandy: we could go back to history, but we don't have a good reason to do it at the moment
        }

        private void MediaControlNextTrackPressed(object sender, object o)
        {
            Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
            {
                // Remove last music from list
                if (_dataSource.MusicList.Count > 0)
                {
                    _dataSource.MusicList.RemoveAt(0);
                }

                // Play next
                PlayNext();
            });
        }

        private void MediaControlStopPressed(object sender, object e)
        {
            Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () => player.Stop());
        }

        private void MediaControlPlayPauseTogglePressed(object sender, object e)
        {
            Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
            {
                switch (player.CurrentState)
                {
                    case MediaElementState.Stopped:
                    case MediaElementState.Paused:
                        player.Play();
                        break;
                    case MediaElementState.Playing:
                        player.Pause();
                        break;
                }
            });
        }

        /// <summary>
        /// Music list collection changed handler
        /// Update current music if new item add to a empty list
        /// </summary>
        /// <param name="sender">Event sender</param>
        /// <param name="e">Event arguments</param>
        private void MusicListCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
        {
            if (e.Action == NotifyCollectionChangedAction.Add && _dataSource.MusicList.Count == 1)
            {
                SetCurrentMusic();
            }
        }

        #endregion

        #region Animation

        private Storyboard _musicTitleStoryboard;

        /// <summary>
        /// MusicTitle textblock size changed event handler
        /// Start or stop marqueeing based on text length
        /// </summary>
        /// <param name="sender">Event sender</param>
        /// <param name="e">Event arguments</param>
        private void MusicTitleSizeChanged(object sender, RoutedEventArgs e)
        {
            var height = musicTitleContainer.ActualHeight - musicTitle.ActualHeight;
            musicTitle.Margin = new Thickness(0, height / 2, 0, 0);

            if (musicTitle.ActualWidth > musicTitleContainer.ActualWidth)
            {
                StartMarqueeing();
            }
            else
            {
                StopMarqueeing();
            }
        }

        private void StopMarqueeing()
        {
            // Stop animation
            if (_musicTitleStoryboard != null)
            {
                _musicTitleStoryboard.Stop();
            }
        }

        public void StartMarqueeing()
        {
            // Define animation
            var doubleAnimation = new DoubleAnimation
            {
                From = musicTitleContainer.ActualWidth,
                To = -musicTitle.ActualWidth,
                RepeatBehavior = RepeatBehavior.Forever,
                Duration = new Duration(TimeSpan.FromSeconds(10)) // Default duration is 10 seconds
            };
            _musicTitleStoryboard = new Storyboard();
            _musicTitleStoryboard.Children.Add(doubleAnimation);
            Storyboard.SetTarget(doubleAnimation, musicTitle);
            Storyboard.SetTargetProperty(doubleAnimation, "(Canvas.Left)");
            // Begin animation
            _musicTitleStoryboard.Begin();
        }

        #endregion

        #region Music control

        /// <summary>
        /// Play next music in the list
        /// Fetch more music if necessary 
        /// </summary>
        private void PlayNext()
        {
            // Try update current music first, music list may be empty we will deal that with another event handler
            SetCurrentMusic();

            // Fetch music list if remaining songs less than 5
            if (_dataSource.MusicList.Count < 5)
            {
                var channel = GetCurrentChannel();
                var music = GetCurrentMusic();
                var channelId = channel != null ? channel.UniqueId : "1"; // Default to channel 1
                var muiscId = music != null ? music.UniqueId : "";
                _dataSource.MoreMusicList(channelId, muiscId);
            }
        }

        #endregion

        #region Current Music / Channel Getter & Setter

        private MusicItemViewModel GetCurrentMusic()
        {
            return (MusicItemViewModel)DefaultViewModel["CurrentMusic"];
        }

        private void SetCurrentMusic()
        {
            if (_dataSource.MusicList.Count > 0)
            {
                DefaultViewModel["CurrentMusic"] = _dataSource.MusicList[0];
            }
        }

        private ChannelItemViewModel GetCurrentChannel()
        {
            return (ChannelItemViewModel)DefaultViewModel["CurrentChannel"];
        }

        private void SetCurrentChannel(ChannelItemViewModel channel)
        {
            _dataSource.CurrentChannel = channel;
            DefaultViewModel["CurrentChannel"] = channel;
        }

        #endregion
    }
}
